home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / newsrc.c < prev    next >
C/C++ Source or Header  |  1996-04-08  |  15KB  |  451 lines

  1. /*
  2.  * Program:    Newsrc manipulation routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    12 September 1994
  13.  * Last Edited:    8 April 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include "misc.h"
  42. #include "newsrc.h"
  43.  
  44. /* Error message
  45.  * Accepts: message format
  46.  *        additional message string
  47.  *        message level
  48.  * Returns: NIL, always
  49.  */
  50.  
  51. static long newsrc_error (fmt,text,errflg)
  52.     char *fmt;
  53.     char *text;
  54.     long errflg;
  55. {
  56.   char tmp[MAILTMPLEN];
  57.   sprintf (tmp,fmt,text);
  58.   mm_log (tmp,errflg);
  59.   return NIL;
  60. }
  61.  
  62.  
  63. /* Write error message
  64.  * Accepts: newsrc name
  65.  *        file designator
  66.  *        file designator
  67.  * Returns: NIL, always
  68.  */
  69.  
  70. static long newsrc_write_error (name,f1,f2)
  71.     char *name;
  72.     FILE *f1;
  73.     FILE *f2;
  74. {
  75.   fclose (f1);            /* close file designators */
  76.   fclose (f2);
  77.   return newsrc_error ("Error writing to %s",name,ERROR);
  78. }
  79.  
  80.  
  81. /* Create newsrc file in local form
  82.  * Accepts: notification flag
  83.  * Returns: file designator of newsrc
  84.  */
  85.  
  86. static FILE *newsrc_create (notify)
  87.     int notify;
  88. {
  89.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  90.   FILE *f = fopen (newsrc,"wb");
  91.   if (!f) newsrc_error ("Unable to create news state %s",newsrc,ERROR);
  92.   else if (notify) newsrc_error ("Creating news state %s",newsrc,WARN);
  93.   return f;
  94. }
  95.  
  96. /* Write new state in newsrc
  97.  * Accepts: file designator of newsrc
  98.  *        group
  99.  *        new subscription status character
  100.  *        newline convention
  101.  * Returns: T if successful, NIL otherwise
  102.  */
  103.  
  104. static long newsrc_newstate (f,group,state,nl)
  105.     FILE *f;
  106.     char *group;
  107.     char state;
  108.     char *nl;
  109. {
  110.   return (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
  111.       ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF) &&
  112.       (fclose (f) != EOF)) ? LONGT : NIL;
  113. }
  114.  
  115.  
  116. /* Write messages in newsrc
  117.  * Accepts: file designator of newsrc
  118.  *        MAIL stream
  119.  *        message number/newsgroup message map
  120.  *        newline convention
  121.  * Returns: T if successful, NIL otherwise
  122.  */
  123.  
  124. static long newsrc_newmessages (f,stream,number,nl)
  125.     FILE *f;
  126.     MAILSTREAM *stream;
  127.                      unsigned long *number;
  128.     char *nl;
  129. {
  130.   unsigned long i,j,k;
  131.   char tmp[MAILTMPLEN];
  132.   int c = ' ';
  133.   for (i = 0,j = 1,k = 0; i < stream->nmsgs; ++i) {
  134.                 /* deleted message? */
  135.     if (mail_elt (stream,i+1)->deleted) {
  136.       k = number[i];        /* this is the top of the current range */
  137.       if (!j) j = k;        /* if no range in progress, start one */
  138.     }
  139.     else if (j) {        /* unread message, ending a range */
  140.       if (k = number[i] - 1) {    /* calculate end of range */
  141.                 /* dump range */
  142.     sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  143.     if (fputs (tmp,f) == EOF) return NIL;
  144.     c = ',';        /* need a comma after the first time */
  145.       }
  146.       j = 0;            /* no more range in progress */
  147.     }
  148.   }
  149.   if (j) {            /* dump trailing range */
  150.     sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
  151.     if (fputs (tmp,f) == EOF) return NIL;
  152.   }
  153.                 /* write trailing newline, return */
  154.   return (fputs (nl,f) == EOF) ? NIL : LONGT;
  155. }
  156.  
  157. /* Find list of newsgroups
  158.  * Accepts: pattern to search
  159.  */
  160.  
  161. void newsrc_find (pat)
  162.     char *pat;
  163. {
  164.   char *s,tmp[MAILTMPLEN];
  165.   int c = ' ';
  166.   char *grp = tmp;
  167.   FILE *f = fopen ((char *) mail_parameters (NIL,GET_NEWSRC,NIL),"rb");
  168.   if (f) {            /* got file? */
  169.                 /* begin with a host specification? */
  170.     if ((*pat == '{') && (s = strchr (pat,'}'))) {
  171.       strcpy (tmp,pat);        /* copy host name */
  172.       grp = tmp + (s + 1 - pat); /* where we write the bboards */
  173.     }
  174.     while (c != EOF) {        /* yes, read newsrc */
  175.       for (s = grp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  176.        (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
  177.        *s++ = c);
  178.       if (c == ':') {        /* found a subscribed newsgroup? */
  179.     *s = '\0';        /* yes, tie off name */
  180.                 /* report if match */
  181.     if (pmatch (tmp,pat)) mm_bboard (tmp);
  182.       }
  183.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  184.     }
  185.     fclose (f);
  186.   }
  187. }
  188.  
  189. /* Update subscription status of newsrc
  190.  * Accepts: group
  191.  *        new subscription status character
  192.  * Returns: T if successful, NIL otherwise
  193.  */
  194.  
  195. long newsrc_update (group,state)
  196.     char *group;
  197.     char state;
  198. {
  199.   char tmp[MAILTMPLEN];
  200.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  201.   long ret = NIL;
  202.   FILE *f = fopen (newsrc,"r+b");
  203.   if (f) {            /* update existing file */
  204.     int c;
  205.     char *s,nl[3];
  206.     long pos = 0;
  207.     nl[0] = nl[1] = nl[2]='\0';    /* no newline known yet */
  208.     do {            /* read newsrc */
  209.       for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  210.        (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
  211.        *s++ = c) pos = ftell (f);
  212.       *s = '\0';        /* tie off name */
  213.                 /* found the newsgroup? */
  214.       if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
  215.     if (c == state) {    /* already at that state? */
  216.       if (c == ':') newsrc_error ("Already subscribed to %s",group,WARN);
  217.       ret = LONGT;        /* noop the update */
  218.     }
  219.                 /* write the character */
  220.     else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
  221.     if (fclose (f) == EOF) ret = NIL;
  222.     f = NIL;        /* done with file */
  223.     break;
  224.       }
  225.                 /* gobble remainder of this line */
  226.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  227.                 /* need to know about newlines and found it? */
  228.       if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
  229.                 /* sniff and see if an LF */
  230.     if ((c = getc (f)) == '\012') nl[1] = c;
  231.     else ungetc (c,f);    /* nope, push it back */
  232.       }
  233.     } while (c != EOF);
  234.  
  235.     if (f) {            /* still haven't written it yet? */
  236.       if (nl[0]) {        /* know its newline convention? */
  237.     fseek (f,0L,2);        /* yes, seek to end of file */
  238.     ret = newsrc_newstate (f,group,state,nl);
  239.       }
  240.       else {            /* can't find a newline convention */
  241.     fclose (f);        /* punt the file */
  242.                 /* can't win if read something */
  243.     if (pos) newsrc_error("Unknown newline convention in %s",newsrc,ERROR);
  244.                 /* file must have been empty, rewrite it */
  245.     else ret = newsrc_newstate (newsrc_create (NIL),group,state,"\n");
  246.       }
  247.     }
  248.   }
  249.                 /* create new file */
  250.   else ret = newsrc_newstate (newsrc_create (T),group,state,"\n");
  251.   return ret;            /* return with update status */
  252. }
  253.  
  254. /* Update newsgroup entry in newsrc
  255.  * Accepts: newsgroup name
  256.  *        MAIL stream
  257.  *        message number/newsgroup message map
  258.  * Returns: number of recent messages
  259.  */
  260.  
  261. long newsrc_read (group,stream,number)
  262.     char *group;
  263.     MAILSTREAM *stream;
  264.     unsigned long *number;
  265. {
  266.   int c;
  267.   char *s,tmp[MAILTMPLEN];
  268.   unsigned long i,j;
  269.   MESSAGECACHE *elt;
  270.   long m = 0,recent = 0,unseen = 0;
  271.   FILE *f = fopen ((char *) mail_parameters (NIL,GET_NEWSRC,NIL),"rb");
  272.   if (f) do {            /* read newsrc */
  273.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
  274.      (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
  275.     *s = '\0';            /* tie off name */
  276.     if ((c==':') || (c=='!')) {    /* found newsgroup? */
  277.       if (strcmp (tmp,group))    /* group name match? */
  278.     while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  279.       else {            /* yes, skip leading whitespace */
  280.     while ((c = getc (f)) == ' ');
  281.                 /* only if unprocessed messages */
  282.     if (stream->nmsgs) while (f && (m < stream->nmsgs)) {
  283.                 /* collect a number */
  284.       if (isdigit (c)) {    /* better have a number */
  285.         for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
  286.         if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
  287.           j = j*10 +(c-'0');/* collect second value if range */
  288.                 /* skip messages before first value */
  289.         while ((m < stream->nmsgs) && (number[m] < i)) {
  290.           if (!unseen) unseen = m + 1;
  291.           m++;        /* next message */
  292.         }
  293.                 /* do all messages in range */
  294.         if (j) while ((m < stream->nmsgs) && (number[m] >= i) &&
  295.               (number[m] <= j)) {
  296.           (elt = mail_elt (stream,++m))->valid = T;
  297.           elt->deleted = T;
  298.         }
  299.         else if ((m < stream->nmsgs) && number[m] == i) {
  300.           (elt = mail_elt (stream,++m))->valid = T;
  301.           elt->deleted = T;
  302.         }
  303.       }
  304.  
  305.       switch (c) {        /* what is the delimiter? */
  306.       case ',':        /* more to come */
  307.         c = getc (f);    /* get first character of number */
  308.         break;
  309.       default:        /* bogus character */
  310.         sprintf (tmp,"Bogus character 0x%x in news state",(int) c);
  311.         mm_log (tmp,ERROR);
  312.       case EOF: case '\015': case '\012':
  313.         fclose (f);        /* all done - close the file */
  314.         f = NIL;
  315.         break;
  316.       }
  317.     }
  318.     else {            /* empty newsgroup */
  319.       while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
  320.       fclose (f);        /* all done - close the file */
  321.       f = NIL;
  322.     }
  323.       }
  324.     }
  325.   } while (f && (c != EOF));    /* until file closed or EOF */
  326.   if (f) {            /* still have file open? */
  327.     sprintf (tmp,"No state for newsgroup %s found, reading as new",group);
  328.     mm_log (tmp,WARN);
  329.     fclose (f);            /* close the file */
  330.   }
  331.   while (m < stream->nmsgs){    /* mark all remaining messages as new */
  332.     (elt = mail_elt (stream,++m))->valid = T;
  333.     elt->recent = T;
  334.     ++recent;            /* count another recent message */
  335.   }
  336.   if (unseen) {            /* report first unseen message */
  337.     sprintf (tmp,"[UNSEEN] %ld is first unseen message in %s",unseen,group);
  338.     mm_notify (stream,tmp,(long) NIL);
  339.   }
  340.   return recent;
  341. }
  342.  
  343. /* Update newsgroup entry in newsrc
  344.  * Accepts: newsgroup name
  345.  *        MAIL stream
  346.  *        message number/newsgroup message map
  347.  * Returns: T if successful, NIL otherwise
  348.  */
  349.  
  350. long newsrc_write (group,stream,number)
  351.     char *group;
  352.     MAILSTREAM *stream;
  353.     unsigned long *number;
  354. {
  355.   int c,d = EOF;
  356.   char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
  357.   char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
  358.   FILE *f,*bf;
  359.   nl[0] = nl[1] = nl[2] = '\0';    /* no newline known yet */
  360.   if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
  361.     if (!(bf = fopen ((strcat (strcpy (backup,newsrc),".old")),"wb"))) {
  362.       fclose (f);        /* punt input file */
  363.       return newsrc_error ("Can't create backup news state %s",backup,ERROR);
  364.     }
  365.                 /* copy to backup file */
  366.     while ((c = getc (f)) != EOF) {
  367.                 /* need to know about newlines and found it? */
  368.       if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
  369.                 /* sniff and see if an LF */
  370.     if ((c = getc (f)) == '\012') nl[1] = c;
  371.     ungetc (c,f);        /* push it back */
  372.       }
  373.                 /* write to backup file */
  374.       if ((d = putc (c,bf)) == EOF) {
  375.     fclose (f);        /* punt input file */
  376.     return newsrc_error("Error writing backup news state %s",newsrc,ERROR);
  377.       }
  378.     }
  379.     fclose (f);            /* close existing file */
  380.     if (fclose (bf) == EOF)    /* and backup file */
  381.       return newsrc_error ("Error closing backup news state %s",newsrc,ERROR);
  382.     if (d == EOF) {        /* open for write if empty file */
  383.       if (f = newsrc_create (NIL)) bf = NIL;
  384.       else return NIL;
  385.     }
  386.     else if (!nl[0])        /* make sure newlines valid */
  387.       return newsrc_error ("Unknown newline convention in %s",newsrc,ERROR);
  388.                 /* now read backup file */
  389.     else if (!(bf = fopen (backup,"rb")))
  390.       return newsrc_error ("Error reading backup news state %s",backup,ERROR);
  391.                 /* open newsrc for writing */
  392.     else if (!(f = fopen (newsrc,"wb"))) {
  393.       fclose (bf);        /* punt backup */
  394.       return newsrc_error ("Can't rewrite news state %s",newsrc,ERROR);
  395.     }
  396.   }
  397.   else {            /* create new newsrc file */
  398.     if (f = newsrc_create (T)) bf = NIL;
  399.     else return NIL;        /* can't create newsrc */
  400.   }
  401.  
  402.   while (bf) {            /* read newsrc */
  403.     for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
  404.      (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
  405.     *s = '\0';            /* tie off name */
  406.                 /* saw correct end of group delimiter? */
  407.     if (tmp[0] && ((c == ':') || (c == '!'))) {
  408.                 /* yes, write newsgroup name and delimiter */
  409.       if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
  410.     return newsrc_write_error (newsrc,bf,f);
  411.       if (!strcmp (tmp,group)) {/* found correct group? */
  412.                 /* yes, write new status */
  413.     if (!newsrc_newmessages (f,stream,number,nl[0] ? nl : "\n"))
  414.       return newsrc_write_error (newsrc,bf,f);
  415.                 /* skip past old data */
  416.     while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'));
  417.                 /* skip past newline */
  418.     while ((c == '\015') || (c == '\012')) c = getc (bf);
  419.     while (c != EOF) {    /* copy remainder of file */
  420.       if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  421.       c = getc (bf);    /* get next character */
  422.     }
  423.                 /* done with file */
  424.     if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,f);
  425.     f = NIL;
  426.       }
  427.                 /* copy remainder of line */
  428.       else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
  429.     if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  430.       if (c == '\015') {    /* write CR if seen */
  431.     if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
  432.                 /* sniff to see if LF */
  433.     if (((c = getc (bf)) != EOF) && (c != '\012')) ungetc (c,bf);
  434.       }
  435.                 /* write LF if seen */
  436.       if ((c == '\012') && (putc (c,f) == EOF))
  437.     return newsrc_write_error (newsrc,bf,f);
  438.     }
  439.     if (c == EOF) {        /* hit end of file? */
  440.       fclose (bf);        /* yup, close the file */
  441.       bf = NIL;
  442.     }
  443.   }
  444.   if (f) {            /* still have newsrc file open? */
  445.     if ((fputs (group,f) == EOF) || ((putc (':',f)) == EOF) ||
  446.     (!newsrc_newmessages (f,stream,number,nl[0] ? nl : "\n")) ||
  447.     (fclose (f) == EOF)) return newsrc_write_error (newsrc,bf,f);
  448.   }
  449.   return LONGT;
  450. }
  451.